home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / memory / xms200je / xmsarray.cpp < prev    next >
C/C++ Source or Header  |  1993-11-22  |  12KB  |  354 lines

  1. //--------------------------------------------------------------------------
  2. //
  3. //      XMSARRAY.CPP: body of XMSarray interface library.
  4. //      Copyright (c) J.English 1993.
  5. //      Author's address: je@unix.brighton.ac.uk
  6. //
  7. //      Permission is granted to use copy and distribute the
  8. //      information contained in this file provided that this
  9. //      copyright notice is retained intact and that any software
  10. //      or other document incorporating this file or parts thereof
  11. //      makes the source code for the library of which this file
  12. //      is a part freely available.
  13. //
  14. //--------------------------------------------------------------------------
  15. //
  16. //      Revision history:
  17. //      1.0     Jun 1993        Initial coding
  18. //      1.01    Sep 1993        Filenames changed from XMS.* to XMSARRAY.*
  19. //      2.0     Nov 1993        Revised to use general purpose XMS class
  20. //
  21. //--------------------------------------------------------------------------
  22.  
  23. #include "xms.h"
  24. #include "xmsarray.h"
  25. #include <dos.h>
  26. #include <stdlib.h>
  27.  
  28. //--------------------------------------------------------------------------
  29. //
  30. //      Constants.
  31. //
  32. const int USED      = 1,    // cache line is in use
  33.           DIRTY     = 2;    // cache line has been written to
  34.  
  35.  
  36. //--------------------------------------------------------------------------
  37. //
  38. //      Data structures.
  39. //
  40. struct XMSbuffer            // cache structure
  41. {
  42.     int      mask;          // ... mask to extract item number in buffer
  43.     long     where;         // ... offset to start of XMS block
  44.     unsigned width;         // ... unit for XMS transfers in bytes
  45.     char*    cache;         // ... buffer for XMS transfers
  46.     long     tag;           // ... current index held in buffer
  47.     char     flags;         // ... buffer state (in use, dirty)
  48. };
  49.  
  50. struct XMSheader            // header for XMS block
  51. {
  52.     long size;              // ... block size in bytes
  53.     int  free;              // ... is it a free block?
  54. };
  55.  
  56. struct XMSmove              // descriptor for XMS moves
  57. {
  58.     long count;             // ... number of bytes to move
  59.     int  srchandle;         // ... source handle (0 for real memory)
  60.     long srcoffset;         // ... source offset (or far pointer)
  61.     int  dsthandle;         // ... destination handle (0 for real memory)
  62.     long dstoffset;         // ... destination offset (or far pointer)
  63. };
  64.  
  65.  
  66. //--------------------------------------------------------------------------
  67. //
  68. //      Globals (statics).
  69. //
  70. static XMS* arrayBlock = 0; // block used to hold XMS arrays
  71. static int  arrayCount = 0; // number of allocated XMS arrays
  72. static long XMSsize    = 0; // current allocation size, internal reckoning
  73.                             // (XMS driver rounds up to nearest 4K or 16K)
  74.  
  75. //--------------------------------------------------------------------------
  76. //
  77. //      Function to return the number of bits required to represent "n",
  78. //      used for rounding up to powers of 2 and generating masks.
  79. //
  80. static int bits (unsigned n)
  81. {
  82.     int b = 0;
  83.     for (long i = 1; i > 0 && i < n; i <<= 1)
  84.         b++;
  85.     return b;
  86. }
  87.  
  88. //--------------------------------------------------------------------------
  89. //
  90. //      XMSlocate: locate a free block of XMS.
  91. //
  92. //      This is used by XMSalloc to check if there is a block of suitable
  93. //      size which has been returned to the free list.  If not, it returns
  94. //      0 (which is never a legitimate offset) and if so, it divides the
  95. //      block as necessary, marks the allocated block as in use and returns
  96. //      its offset.  XMS blocks are prefaced by an XMSheader giving their
  97. //      size and status (free/in use).  Offsets refer to the first byte
  98. //      of the allocation (i.e. the byte after the header); the size then
  99. //      gives the address of the next block.  Adjacent free blocks are merged
  100. //      while scanning the list.  If the last block is free, it can't be
  101. //      merged with anything and so XMSsize is adjusted to remove it from
  102. //      the internal record of the allocation size.
  103. //
  104. static long XMSlocate (long size)
  105. {
  106.     long p = 0, oldp = 0;
  107.     XMSheader h, oldh = {0,0};
  108.  
  109.     //--- scan block list from offset 0
  110.     while (p < XMSsize)
  111.     {
  112.         //--- get header of next block
  113.         XMS::copy (&h, (*arrayBlock)[p], sizeof(h));
  114.  
  115.         //--- merge adjacent free blocks by changing size of previous block
  116.         if (oldh.free && h.free)
  117.         {   oldh.size += h.size + sizeof(h);
  118.             h = oldh, p = oldp;
  119.             XMS::copy ((*arrayBlock)[p], &h, sizeof(h));
  120.         }
  121.  
  122.         //--- check if block (after possible merging) is big enough
  123.         if (h.free && h.size >= size + sizeof(XMSheader))
  124.             break;
  125.  
  126.         //--- keep value and offset for block and get offset of next header
  127.         oldh = h, oldp = p;
  128.         p += h.size + sizeof(h);
  129.     }
  130.  
  131.     //--- no suitable block available: remove last block if it is free
  132.     if (p >= XMSsize)
  133.     {   if (oldh.free)
  134.             XMSsize = oldp;
  135.         return 0;
  136.     }
  137.  
  138.     //--- divide block if necessary
  139.     if (h.free && h.size >= size + sizeof(XMSheader))
  140.     {
  141.         XMSheader t = {h.size - size - sizeof(t), 1};
  142.         XMS::copy (arrayBlock->at(p+sizeof(h)+size), &t, sizeof(t));
  143.         h.size = size;
  144.     }
  145.  
  146.     //--- write new block's header and return offset of first usable byte
  147.     h.free = 0;
  148.     XMS::copy (arrayBlock->at(p), &h, sizeof(h));
  149.     return p + sizeof(h);
  150. }
  151.  
  152. //--------------------------------------------------------------------------
  153. //
  154. //      XMSalloc: allocate a block of XMS.
  155. //
  156. //      This function tries to allocate "size" bytes of XMS and returns 0
  157. //      if it fails.
  158. //
  159. static long XMSalloc (long size)
  160. {
  161.     //--- check XMS initialised before proceeding
  162.     if (arrayCount++ == 0)
  163.     {   arrayBlock = new XMS (sizeof(XMSheader) + size);
  164.         if (arrayBlock == 0 || !arrayBlock->valid())
  165.         {   delete arrayBlock;
  166.             arrayCount--;
  167.             arrayBlock = 0;
  168.         }
  169.         else
  170.         {   XMSheader h = {0,0};
  171.             XMS::copy (arrayBlock->at(0), &h, sizeof(XMSheader));
  172.         }
  173.     }
  174.     if (arrayBlock == 0)
  175.         return 0;
  176.  
  177.     //--- try to find a suitable free block in the current allocation
  178.     long pos = XMSlocate (size);
  179.     if (pos != 0)
  180.         return pos;
  181.  
  182.     //--- try to extend current allocation if none found
  183.     if (arrayBlock->resize (XMSsize + size + sizeof(XMSheader)) != XMS::SUCCESS)
  184.         return 0;
  185.  
  186.     //--- write the header for the new block at the end of current allocation
  187.     XMSheader h = {size,0};
  188.     XMS::copy (arrayBlock->at(XMSsize), &h, sizeof(h));
  189.  
  190.     //--- get offset of first usable byte of new block
  191.     pos = XMSsize + sizeof(h);
  192.  
  193.     //--- increase allocation size to accomodate it and return its offset
  194.     XMSsize += size + sizeof(h);
  195.     return pos;
  196. }
  197.  
  198.  
  199. //--------------------------------------------------------------------------
  200. //
  201. //      XMSfree: mark a block of XMS as free.
  202. //
  203. //      This returns a block to the free list by rewriting its header.
  204. //
  205. static void XMSfree (long offset)
  206. {
  207.     if (--arrayCount == 0)
  208.     {   delete arrayBlock;
  209.         return;
  210.     }
  211.     XMSheader h;
  212.     XMS::copy (&h, arrayBlock->at(offset-sizeof(h)), sizeof(h));
  213.     h.free = 1;
  214.     XMS::copy (arrayBlock->at(offset-sizeof(h)), &h, sizeof(h));
  215. }
  216.  
  217. //--------------------------------------------------------------------------
  218. //
  219. //      XMSarrayBase::XMSarrayBase.
  220. //
  221. //      Constructor for base class XMS, called by constructor for XMSarray.
  222. //      Constructs an XMS array containing the specified number of items of
  223. //      the specified size and also a one-line cache of the specified size.
  224. //
  225. XMSarrayBase::XMSarrayBase (long items, unsigned size, unsigned cachesize)
  226. {
  227.     //--- initial state is "unallocated"
  228.     state = 0;
  229.  
  230.     //--- create the cache
  231.     buffer = new XMSbuffer;
  232.     if (buffer == 0)
  233.         return;
  234.  
  235.     //--- set up the item offset mask (items in line - 1)
  236.     buffer->mask = (size > cachesize) ? 0 : 
  237.                    (1 << (bits(cachesize) - bits(size))) - 1;
  238.  
  239.     //--- calculate number of items needed (1 extra rounded up to nearest line)
  240.     if (items < 0)
  241.         items = 0;
  242.     items += buffer->mask;
  243.     items &= ~buffer->mask;
  244.  
  245.     //--- calculate cache size in bytes and allocate it
  246.     buffer->width = (buffer->mask + 1) * size;
  247.     buffer->cache = new char [buffer->width];
  248.     if (buffer->cache == 0)
  249.         return;
  250.  
  251.     //--- try to allocate the XMS block
  252.     buffer->where = XMSalloc (items * size);
  253.  
  254.     //--- mark cache line as unused
  255.     buffer->tag   = 0;
  256.     buffer->flags = 0;
  257.  
  258.     //--- record if allocation succeeded
  259.     state = (buffer->where != 0);
  260. }
  261.  
  262.  
  263. //--------------------------------------------------------------------------
  264. //
  265. //      XMSarrayBase::XMSarrayBase.
  266. //
  267. //      Copy constructor used by XMSitem to clone a reference to the
  268. //      base array.
  269. //
  270. XMSarrayBase::XMSarrayBase (const XMSarrayBase& base)
  271. {
  272.     state  = base.state;
  273.     buffer = base.buffer;
  274. }
  275.  
  276. //--------------------------------------------------------------------------
  277. //
  278. //      XMSarrayBase::free.
  279. //
  280. //      Try to free an array.  Check that it's there before doing anything,
  281. //      then free the XMS block and delete the cache.  This is not a written
  282. //      as a destructor so that XMSitem doesn't call it.  Unfortunately
  283. //      Borland C++ won't let you nominate all possible instantiations of
  284. //      a template as friends of a non-template class, i.e.
  285. //          class X { template<class T> friend class Y<T>; ... };
  286. //      gives a compilation error.
  287. //
  288. void XMSarrayBase::free ()
  289. {
  290.     if (buffer == 0)
  291.         return;
  292.     if (buffer->where != 0)
  293.         XMSfree (buffer->where);
  294.     delete buffer->cache;
  295.     delete buffer;
  296. }
  297.  
  298.  
  299. //--------------------------------------------------------------------------
  300. //
  301. //      XMSarrayBase::mask.
  302. //
  303. //      Return the item number within the cache line for a given subscript.
  304. //
  305. int XMSarrayBase::mask (int n)
  306. {
  307.     if (!state)
  308.         return 0;
  309.     return n & buffer->mask;
  310. }
  311.  
  312. //--------------------------------------------------------------------------
  313. //
  314. //      XMSarrayBase::get.
  315. //
  316. //      Get a block from XMS into conventional memory.  If it's not in the
  317. //      cache, load the relevant line (writing back the old line if it was
  318. //      dirty).  Return a pointer to the cached copy of the item.  "Base"
  319. //      is the base address of the line to load and "offset" is the offset
  320. //      into the cache buffer.
  321. //
  322. void* XMSarrayBase::get (long base, int offset)
  323. {
  324.     if (!state)
  325.         return 0;
  326.     if (buffer->tag != base || !(buffer->flags & USED))
  327.     {   if (buffer->flags & DIRTY)
  328.             XMS::copy (arrayBlock->at (buffer->where + buffer->tag),
  329.                        buffer->cache, buffer->width);
  330.         XMS::copy (buffer->cache, arrayBlock->at (buffer->where + base),
  331.                    buffer->width);
  332.         buffer->flags = USED;
  333.         buffer->tag = base;
  334.     }
  335.     return buffer->cache + offset;
  336. }
  337.  
  338.  
  339. //--------------------------------------------------------------------------
  340. //
  341. //      XMSarrayBase::put.
  342. //
  343. //      Gets a copy of the relevant cache line into memory and then marks
  344. //      the line as dirty (about to be written to).
  345. //
  346. void* XMSarrayBase::put (long base, int offset)
  347. {
  348.     if (!state)
  349.         return 0;
  350.     void* addr = get (base, offset);
  351.     buffer->flags |= DIRTY;
  352.     return addr;
  353. }
  354.